#!/usr/bin/env python
# -*- coding: utf-8 -*-

#    Copyright © 2011 Jaume Delclòs Coll (cosarara97)

#    This file is part of pyFSF.

#    pyFSF is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    pyFSF is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with pyFSF.  If not, see <http://www.gnu.org/licenses/>.


import sys
from romHacking import *

 # Import Qt modules
from PyQt4 import QtCore, QtGui

 # Import the compiled UI module
from window import Ui_MainWindow


#----- Variables Globals -----

fcontents = None
fileName = None
start = None
lenght = None
#------------------------------


 # Main Class
class Main(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Connections
        self.connect(self.ui.search_button, QtCore.SIGNAL('clicked()'),
                    self.search)  # Just calls the search function when
                                  # search_button is pressed.
        self.connect(self.ui.actionOpen, QtCore.SIGNAL('triggered()'),
                    self.openROM)  # You can figure it out. (I think :D)
        self.connect(self.ui.actionQuit, QtCore.SIGNAL('triggered()'),
                    self.dw)    # I do it this way because putting
                                # quit() directly doesn't work and
                                # I'm too lazy to find a better way...

        # Clas variables
        self.fileName = None
        self.hexmode = False

    # ---Functions---

    # This checks if we are in hex or hex mode
    def check_mode(self):
        if self.ui.radio_Hex.isChecked():
            self.hexmode = True
        else:
            self.hexmode = False

    # The search function.
    def search(self):
        self.check_mode()  # Checks if we are in dec or hex mode
        if not self.fileName:  # Checks if we selected a file
            print "Error: No input file"  # Prints it in the shell
                                          # (if we have one)
            self.ui.found_label.setText("No input file")  # Sets the info
                              # label to: "No input file"
            return            # And quits the function

        # Ok, if we had an input file we contnue...
        # If we are in hex mode (self.hexmode = True)
        if self.hexmode:
            # And if we have input a lenght in it's lineEdit
            if self.ui.lineEdit_lenght.text():
                # We put it's contents in self.lenght converted to dec
                self.lenght = conv_hex2dec(self.ui.lineEdit_lenght.text())
            # If we hadn't input the lenght
            else:
                # Tell the user what he has done wrong
                print "Error: Lenght not specified"
                self.ui.found_label.setText("Lenght not specified")
                return  # And quit the function
        # If we weren't in hex mode (so we were in dec mode)
        else:
            # And we had input a lenght
            if self.ui.lineEdit_lenght.text():
                # Put it in the self.lenght variable, converted to int
                self.lenght = int(self.ui.lineEdit_lenght.text())
            # If we hadn't input a lenght
            else:
                # Tell the user what he has done wrong
                print "Error: Lenght not specified"
                self.ui.found_label.setText("Lenght not specified")
                return  # And quit the function

        # We put the start offset in the self.start variable. It will
        # always be in Hex, because ROM hackers never use decimal
        # offsets...
        self.start = str(self.ui.lineEdit_offset.text())

        # If there was nothing written in start we won't throw error,
        # we'll assume the user wants to search from the beggining, so
        # it will be 0.
        if not self.start:
            self.start = 0
        self.fcontents2 = convn_h2a(self.fcontents)

 #--------------------This checks if specified length is too long =P------------
        # Calculates the total lenght of the ROM. It is the lenght of the
        # string containing it /2, because every byte is 2 characters.
        total_rom_lenght = len(self.fcontents2) / 2
        # If the total ROM lenght is smaller than the lenght we specified
        if total_rom_lenght < self.lenght:
            # We tell the user
            print "Error: Lenght is too long"
            self.ui.found_label.setText("Lenght is too long")
            return  # And quit the function
 #------------------------------------------------------------------------------

        # This actually searches our free space.
        # It uses the function buscar, searching ff as much times as
        # the lenght says, and from the offset *2, because bytes are
        # 2 caracters.
        self.offset_found = self.buscar("ff", self.lenght,
                                        conv_hex2dec(self.start) * 2)

        # OK, so no we have searched for the free space. Now we must check
        # the result.

        # When we use the function find (we use inside buscar()) and it
        # Finds nothing, it returns -1 (int). So we check it.
        if self.offset_found == -1:
            # And if it's true we tell the user and quit the function.
            print "Error: Not found"
            self.ui.found_label.setText("Not found")
            return
        else:
            # But if we found it, we convert it to hex and continue.
            self.offset_fond_str = str(conv_dec2hex(self.offset_found))

        # So if the program is here is because everythng went,
        # The user did everything he had to do and the free space was
        # found.

        # We print it in the shell (if there is one, of course)
        print "Space with " + str(self.lenght) + " FF's in offset "\
         + self.offset_fond_str
        # And write it in our label.
        self.ui.found_label.setText("offset: 0x" + self.offset_fond_str)

    #--------functions--------
    # This is I couldn't connect the quit signal to quit() directly...
    def dw(self):
        quit()

    # This opens the "open file" dialog, and puts ROM contents into
    # self.fcontents variable.
    def openROM(self):
        self.fileName = QtGui.QFileDialog.getOpenFileName(self, "Open ROM", "~",
                                               "GBA ROM (*.gba);;All Files (*)")
        if self.fileName == "":
            return
        print self.fileName
        f = open(self.fileName, "rb")
        self.fcontents = f.read()
        f.close()

    # This function is made to find a specified byte a number of times
    # in a varible which contains a ROM in Hex.
    def buscar(self, byte, lenght, start):
        sub = byte * lenght  # Sub = byte a buscar (ff) * longitud a buscar
        offset_found = self.fcontents2.find(sub, start) / 2
        return offset_found


#    # This function converts ascii to hex. I made it because I never
#    # remember how to use binascii...
#def conv_a2h(asciistr):
#    hexstr = binascii.b2a_hex(asciistr)
#    return hexstr
#
#
#    # The same as previous, but hex to ascii
#def convn_h2a(hexstr):
#    asciistr = binascii.b2a_hex(hexstr)
#    return asciistr
#
#
#    # Converts a decimal integrer to an hex string. (Without 0x)
#def conv_dec2hex(decnum):
#    hexnum = hex(decnum)[2:]
#    return hexnum
#
#
#    # Converts an hex string (if it's not astring it converts it to one)
#    # to a decimal integrer which we can work with.
#def conv_hex2dec(hexnum):
#    decnum = int(str(hexnum), 16)
#    return decnum


    # And finally, the main function of the program. It's called when
    # You are running this file directly, instead of using it as a module,
    # so it's always called :D
def main():
    # This is Qt4 stuff...
    app = QtGui.QApplication(sys.argv)
    window123 = Main()
    window123.show()
    sys.exit(app.exec_())

# Its what I explained before. It checks if this file is being run
# direcly, and if it is, it calls main.
if __name__ == "__main__":
    main()
